'''
This will combine some basic data handling, plotting, and writing functions.
You will probably want to have the docs/help open to support you!

The base code is provided at the bottom - you can add to this, change it, or remove it and write your own if you prefer!

You are welcome to add more options to the menu and create your own graphs - think of how you can use the different styles of graph.

For example - you could add box and whisker diagrams to show averages, or scatter plots to show outliers.

The data set 'ramen-ratings.csv' draws from a popular blog rating instant ramen products.
'''
import pandas as pd
import matplotlib.pyplot as plt
import random


def show_histogram_of_ramen_scores(data):
    plt.hist(bins=20, color='skyblue', alpha=0.7)#bins=20将数据分为20个区间，alpha是透明度
    plt.title('distribution of ramen scores')
    plt.xlabel('Stars')
    plt.ylabel('Frequency')
    plt.grid(axis='y', alpha=0.75)#grid添加网格线 axis='y'：只在y轴方向显示网格线
    plt.show()

def show_bar_chart_stars_per_manufacturer(data):
    manufacturer_ratings = data.groupby('Brand')['Stars'].mean().sorted_value(ascending=False)
    
    # 只显示前20个制造商，避免图表过于拥挤
    top_manufacturers = manufacturer_ratings.head(20)

    top_manufacturers.plot(kind='bar', color='skyblue')
    plt.title('average stars of manufacturer')
    plt.xlabel('manufacturer')
    plt.ylabel('average stars')
    #plt.xticks(rotation=45, ha='right')：将X轴标签旋转45度，右对齐
    #plt.tight_layout()：自动调整子图参数，使之填充整个图像区域
    plt.show()
def show_bar_chart_stars_per_country(data):
    country_ratings = data.groupby('Country')['Stars'].mean().sorted_value(ascending=False)
    
    # 只显示前20个国家，避免图表过于拥挤
    top_countries =country_ratings.head(20)

    top_countries.plot(kind='bar', color='skyblue')
    plt.title('average stars of manufacturer')
    plt.xlabel('country')
    plt.ylabel('average stars')
   #plt.xticks(rotation=45, ha='right')：将X轴标签旋转45度，右对齐
   #plt.tight_layout()：自动调整子图参数，使之填充整个图像区域
    plt.show()
def find_best_per_country(data, country):
    # 筛选指定国家的数据
    country_data = data['Country']

    # 找到最高评分
    max_rating = country_data['Stars'].max()
    
    # 找到最高评分对应的行
    best_ramen = country_data[country_data['Stars'] == max_rating]
    
    # 如果有多个相同评分的，选择第一个
    best_index = best_ramen.index[0]
    

    print(f"Best ramen in {country}:")
    print_ramen_rating(data.loc[best_index])#data.loc[best_index]：通过索引获取整行数据
    
    return best_index

def find_worst_per_country(data, country):
    # 筛选指定国家的数据
    country_data = data['Country']

    # 找到最低评分
    min_rating = country_data['Stars'].min()
    
    # 找到最低评分对应的行
    worst_ramen = country_data[country_data['Stars'] == min_rating]
    
    # 如果有多个相同评分的，选择第一个
    worst_index = worst_ramen.index[0]
    
    print(f"Worst ramen in {country}:")
    print_ramen_rating(data.loc[worst_index])

    return worst_index

def show_piechart_of_manufacturers_by_country(data, country):
    '''
    Create a pie chart showing the percentage of each manufacturer in the country
    '''
    country_data = data['country']
    manufacturer_counts = country_data['Brand'].value_counts()

    top_manufacturers = manufacturer_counts.head(10)

    plt.pie(top_manufacturers.values, labels=top_manufacturers.index, autopct='%1.1f%%', startangle=90)
    #top_manufacturers.values：饼图各扇区的大小
    #labels=top_manufacturers.index：饼图各扇区的标签
    #autopct='%1.1f%%'：显示百分比，保留一位小数
    #startangle=90：从90度开始绘制饼图
    plt.title(f'Manufacturer Distribution in {country}')
    plt.axis('equal')  # 确保饼图是圆形
    plt.show()

def print_ramen_rating(rating):
    '''
    Print out a row from the dataframe ('rating') in a more human-readable format, e.g.:

    Name: 
    Type of ramen:
    Manufacturer:
    Country of Origin:
    Stars:
    '''
    
    print("/n" + "-"*50)
    print(f"Name:{rating['Varity']}")
    print(f"Type of ramen:{rating['Style']}")
    print(f"Manufacturer:{rating['Brand']}")
    print(f"Country of Origin:{rating['Country']}")
    print(f"Stars:{rating['Stars']}")
    print("/n" + "-"*50)


def show_top_ten(data):
    '''
    Select the top-ten ramen and print them out in order of lowest rating to highest
    You may want to call your print_ramen_rating() function for this.
    '''
    # 获取评分最高的10个拉面（按评分从高到低排列）
    top_ten = data.nlargest(10, 'Stars')
    
    print("Top 10 Ramen Ratings (from 10th to 1st):")
    print("="*60)
    
    # 从第10名到第1名显示（评分从低到高）
    for i in range(len(top_ten)-1, -1, -1):
        rank = i + 1  
        print(f"\n#{rank}:")
        print_ramen_rating(top_ten.iloc[i])
    '''
    len(top_ten):获取top_ten的长度,应该是10

    len(top_ten)-1:起始值,9(因为索引从0开始)

    -1:结束值（不包含）,所以到0为止

    -1:步长,每次减1

    生成的序列是：[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    '''

def random_ramen_review(data):
    '''
    随机选择一款拉面并显示其信息，包括与国家和制造商平均分的比较
    '''
    # 随机选择一行
    random_index = random.randint(0, len(data)-1)
    random_ramen = data.iloc[random_index]
    
    print("Random Ramen Review:")
    print("="*50)
    print_ramen_rating(random_ramen)
    
    # 计算比较信息
    country = random_ramen['Country']
    manufacturer = random_ramen['Brand']
    ramen_stars = random_ramen['Stars']
    
    # 计算国家平均分
    country_avg = data[data['Country'] == country]['Stars'].mean()
    country_comparison = "above" if ramen_stars > country_avg else "below"
    
    # 计算制造商平均分
    manufacturer_avg = data[data['Brand'] == manufacturer]['Stars'].mean()
    manufacturer_comparison = "above" if ramen_stars > manufacturer_avg else "below"
    
    print("Comparison Analysis:")
    print(f"- This ramen is {country_comparison} the {country} average ({country_avg:.2f} stars)")
    print(f"- This ramen is {manufacturer_comparison} the {manufacturer} average ({manufacturer_avg:.2f} stars)")
    
    # 在直方图上显示该拉面的位置
    plt.figure(figsize=(12, 5))
    
    # 子图1：总体分布
    plt.subplot(1, 2, 1)#创建1行2列的子图网格，并选择第一个子图
    plt.hist(data['Stars'], bins=20, color='skyblue', alpha=0.7, edgecolor='black')#plt.hist()绘制直方图
    plt.axvline(ramen_stars, color='red', linestyle='--', linewidth=2, 
                label=f'Selected Ramen: {ramen_stars} stars')#在图表上绘制垂直参考线
    plt.title('Overall Distribution with Selected Ramen')
    plt.xlabel('Stars')
    plt.ylabel('Frequency')
    plt.legend()#显示图例
    plt.grid(axis='y', alpha=0.75)#grid添加网格线 axis='y'：只在y轴方向显示网格线
    
    # 子图2：国家内的分布
    plt.subplot(1, 2, 2)
    country_data = data[data['Country'] == country]
    plt.hist(country_data['Stars'], bins=15, color='lightgreen', alpha=0.7, edgecolor='black')
    plt.axvline(ramen_stars, color='red', linestyle='--', linewidth=2, 
                label=f'Selected: {ramen_stars} stars')
    plt.axvline(country_avg, color='blue', linestyle='--', linewidth=2, 
                label=f'Country Avg: {country_avg:.2f} stars')
    plt.title(f'Distribution in {country}')
    plt.xlabel('Stars')
    plt.ylabel('Frequency')
    plt.legend()
    plt.grid(axis='y', alpha=0.75)
    
    plt.tight_layout()#自动调整子图参数，使子图适合图形区域，避免重叠
    plt.show()

def show_box_plot_by_country(data):
    """
    新增功能：按国家显示评分的箱线图
    箱线图可以显示数据的分布、中位数、异常值等
    """
    # 选择评分数量最多的前15个国家
    top_countries = data['Country'].value_counts().head(15).index
    filtered_data = data[data['Country'].isin(top_countries)]
    '''
    data['Country'].isin(top_countries):创建一个布尔序列,标记哪些行的国家在top_countries列表中

    data[布尔序列]:使用布尔索引筛选数据,只保留属于top_countries中国家的数据

    这行代码创建一个新的DataFrame,只包含前15个国家的数据
    '''
    
    # 创建箱线图
    plt.figure(figsize=(15, 8))
    filtered_data.boxplot(column='Stars', by='Country', vert=False, grid=False)
    '''
    filtered_data.boxplot():pandas的箱线图方法

    column='Stars'：指定要绘制箱线图的列是'Stars'（评分）

    by='Country'：按'Country'列分组，为每个国家创建一个箱线图

    vert=False:设置箱线图为水平方向(False)而不是垂直方向(True)

    grid=False:不显示网格线
    '''
    plt.title('Ramen Ratings Distribution by Country (Top 15)')
    plt.suptitle('')  # 移除默认的副标题
    plt.xlabel('Stars')
    plt.tight_layout()#自动调整子图参数，使子图适合图形区域，避免重叠
    plt.show()

def show_scatter_plot_style_vs_stars(data):
    """
    新增功能：显示拉面类型与评分的散点图
    散点图可以显示两个变量之间的关系
    """
    # 计算每种类型的平均评分和数量
    style_stats = data.groupby('Style').agg({
        'Stars': ['mean', 'count'],
        'Brand': 'first'
    }).round(2)
    '''
    data.groupby('Style')：按'Style'列（拉面类型）分组

    .agg({...})：对每个组应用聚合函数

    'Stars': ['mean', 'count']：对'Stars'列计算平均值和计数

    'Brand': 'first'：取每个组中第一个出现的品牌（这里可能只是为了获取一个代表性品牌）

    .round(2)：将结果四舍五入到小数点后两位

    这行代码创建一个新的DataFrame,包含每种拉面类型的平均评分、样本数量和代表性品牌
    '''
    # 简化列名
    style_stats.columns = ['Mean_Stars', 'Count', 'Brand']
    style_stats = style_stats.reset_index()
    '''
    reset_index()：将分组键（'Style'）从索引转换回普通列

    这样'Style'就成为一个普通的列，可以在图表中使用
    '''
    plt.figure(figsize=(12, 8))
    scatter = plt.scatter(style_stats['Mean_Stars'], range(len(style_stats)), 
                         s=style_stats['Count']*10, alpha=0.6, 
                         c=style_stats['Mean_Stars'], cmap='viridis')
    '''
    plt.scatter()：创建散点图

    style_stats['Mean_Stars']:X轴数据,使用平均评分

    range(len(style_stats)):Y轴数据,使用0到类型数量-1的序列

    s=style_stats['Count']*10:点的大小,基于样本数量乘以10(放大差异)

    alpha=0.6:设置点的透明度为60%

    c=style_stats['Mean_Stars']：点的颜色，基于平均评分

    cmap='viridis':颜色映射,使用viridis色彩方案

    scatter =：将散点图对象保存到变量，以便后续使用
    '''
    # 添加标签
    for i, row in style_stats.iterrows():
        plt.annotate(f"{row['Style']}\n({row['Count']} samples)", 
                    (row['Mean_Stars'], i), 
                    xytext=(5, 0), textcoords='offset points',
                    fontsize=9, alpha=0.8)
    '''
    style_stats.iterrows():遍历DataFrame的每一行
    i:行索引
    row:包含该行所有数据的Series对象

    plt.annotate()：在图表上添加文本注释

    f"{row['Style']}\n({row['Count']} samples)"：注释文本，包含拉面类型和样本数量，用换行符分隔

    (row['Mean_Stars'], i):注释指向的坐标(X=平均评分,Y=行索引）

    xytext=(5, 0):文本相对于点的偏移量(向右5点,垂直不偏移)

    textcoords='offset points':指定偏移量的单位是点(points)

    fontsize=9:设置字体大小为9

    alpha=0.8:设置文本透明度为80%
    '''
    plt.colorbar(scatter, label='Average Stars')
    plt.title('Ramen Styles vs Average Stars (Bubble size = sample count)')
    plt.xlabel('Average Stars')
    plt.ylabel('Ramen Style')
    plt.yticks([])  # 移除Y轴刻度
    plt.grid(axis='x', alpha=0.3)#axis='x'：只在X轴方向显示网格线
    plt.tight_layout()
    plt.show()

def menu():
    ''' 显示菜单并允许用户选择 '''
    print("\n" + "="*50)
    print("~ Ramen Rating Database ~")
    print("="*50)
    print("1 - See distribution of scores")
    print("2 - See average score per country")
    print("3 - See average score per manufacturer") 
    print("4 - Get a breakdown of a specific country")
    print("5 - See the top ten ramens")
    print("6 - See a random Ramen")
    print("7 - Box plot by country (NEW)")
    print("8 - Scatter plot: Style vs Stars (NEW)")
    print("Q - Quit")
    
    choice = ""
    while choice not in ["1", "2", "3", "4", "5", "6", "7", "8", "Q"]:
        choice = input("\nPick an option: ").upper()
    return choice

# 主程序
def main():
    # 读取数据
    try:
        data = pd.read_csv(r"C:\Users\86189\Downloads\week-8-session-1-main\week-8-session-1-main\activity-3-mpl\ramen-ratings.csv")
        print(f"Data loaded successfully! {len(data)} records found.")
        
        # 数据预处理：确保Stars列是数值类型
        if data['Stars'].dtype == 'object':
            # 如果有非数值数据，尝试转换
            data['Stars'] = pd.to_numeric(data['Stars'], errors='coerce')
            # 删除包含NaN的行
            data = data.dropna(subset=['Stars'])
            print(f"After cleaning: {len(data)} records remaining.")
            
    except FileNotFoundError:
        print("Error: 'ramen-ratings.csv' file not found!")
        print("Please make sure the file is in the same directory as this script.")
        return
    except Exception as e:
        print(f"Error loading data: {e}")
        return

    while True:
        choice = menu()
        match choice:
            case "1":
                show_histogram_of_ramen_scores(data)
            case "2":
                show_bar_chart_stars_per_country(data)
            case "3":
                show_bar_chart_stars_per_manufacturer(data)
            case "4":
                country = input("Enter country: ").title()
                find_best_per_country(data, country)
                find_worst_per_country(data, country)
                show_piechart_of_manufacturers_by_country(data, country)
            case "5":
                show_top_ten(data)
            case "6":
                random_ramen_review(data)
            case "7":
                show_box_plot_by_country(data)
            case "8":
                show_scatter_plot_style_vs_stars(data)
            case "Q":
                print("Thank you for using the Ramen Rating Database!")
                break

if __name__ == "__main__":
    main()
